﻿using System;
using System.Collections.Generic;
using System.Text;

namespace PCR.Logic
{
    using PCR.Base;
    
    /// <summary>
    /// Virus type xml database
    /// </summary>
    internal class XmlVirusType : IVirusTypeAccess
    {
        /// <summary>
        /// Xml tag definition
        /// </summary>
        static readonly string VirusTypes_Tag = "VirusTypes";
        static readonly string VirusType_Tag = "VirusType";
        static readonly string ObjectID_Tag = "ObjectID";
        static readonly string Name_Tag = "Name";
        static readonly string EVal_Tag = "EVal";
        static readonly string KVal_Tag = "KVal";
        static readonly string BVal_Tag = "BVal";
        static readonly string DefineType_Tag = "DefineType";
        static readonly string BaseCellCount_Tag = "BaseCellCount";
        static readonly string VirusSafetyExponent_Tag = "VirusSafetyExponent";
        static readonly UInt32 Default_BaseCellCount = 10000;
        static readonly double Default_VirusSafetyExponent = 1.0;

        protected System.Xml.XmlDocument _doc;
        protected string _filename;
        
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="doc">xml doccument</param>
        /// <param name="filename">xml filename, used to store data to the file</param>
        public XmlVirusType(System.Xml.XmlDocument doc,
            string filename)
        {
            System.Diagnostics.Debug.Assert(doc != null);
            System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(filename));

            this._doc = doc;
            this._filename = filename;
        }

        /// <summary>
        /// Append a object
        /// </summary>
        /// <param name="item">Object instance to append</param>
        public void Append(IVirusType item)
        {
            System.Diagnostics.Debug.Assert(item != null);

            // First get the <VirusTypes> node
            System.Xml.XmlNode nodeVirusTypes = GetVirusTypesNode();
            System.Diagnostics.Debug.Assert(nodeVirusTypes != null);

            AppendInternal(item, nodeVirusTypes);
            Save();
        }

        /// <summary>
        /// Append a list of object
        /// </summary>
        /// <param name="items">Object list to append</param>
        public void Append(IList<IVirusType> items)
        {
            System.Diagnostics.Debug.Assert(items != null);
            if (items.Count == 0)
                return;

            // First get the <VirusTypes> node
            System.Xml.XmlNode nodeVirusTypes = GetVirusTypesNode();
            System.Diagnostics.Debug.Assert(nodeVirusTypes != null);

            foreach (IVirusType item in items)
            {
                AppendInternal(item, nodeVirusTypes);
            }

            Save();
        }

        /// <summary>
        /// Update the object
        /// </summary>
        /// <param name="item">Object instance to update</param>
        public void Update(IVirusType item)
        {
            System.Diagnostics.Debug.Assert(item != null);
            UpdateByID(item.ObjectID, item);
        }

        /// <summary>
        /// Update the object by ID
        /// </summary>
        /// <param name="id">Object ID</param>
        /// <param name="item">Object instance to update</param>
        public void UpdateByID(Guid id, IVirusType item)
        {
            // Find the specified node
            System.Xml.XmlNode virusTypeNode = FindNodeByID(item.ObjectID);
            System.Diagnostics.Debug.Assert(virusTypeNode != null);
            virusTypeNode.RemoveAll();
            FillNodeWithVirusType(item, virusTypeNode);
            Save();
        }

        /// <summary>
        /// Delete specified object
        /// </summary>
        /// <param name="item">Object instance to delete</param>
        public void Delete(IVirusType item)
        {
            System.Diagnostics.Debug.Assert(item != null);
            DeleteByID(item.ObjectID);
        }

        /// <summary>
        /// Delete list of objects
        /// </summary>
        /// <param name="items">Objects to delete</param>
        public void Delete(IList<IVirusType> items)
        {
           
        }

        /// <summary>
        /// Delete object by ID
        /// </summary>
        /// <param name="id">Object ID to delete</param>
        public void DeleteByID(Guid id)
        {
            System.Xml.XmlNode nodeVirusTypes = GetVirusTypesNode();
            System.Diagnostics.Debug.Assert(nodeVirusTypes != null);

            // Find the specified node
            System.Xml.XmlNode nodeVirusType = FindNodeByID(id);
            System.Diagnostics.Debug.Assert(nodeVirusType != null);

            nodeVirusTypes.RemoveChild(nodeVirusType);

            Save();
        }

        /// <summary>
        /// Delete all objects
        /// </summary>
        public void DeleteAll()
        {
            System.Xml.XmlNode nodeVirusTypes = GetVirusTypesNode();
            nodeVirusTypes.RemoveAll();
            Save();
        }

        /// <summary>
        /// Select all the objects
        /// </summary>
        /// <returns></returns>
        public IList<IVirusType> SelectAll()
        {
            IList<IVirusType> virusTypes = new List<IVirusType>();

            // First get the <VirusTypes> node, 
            // and this may create a new node for it
            // it does not affect us to do the following operations
            System.Xml.XmlNode nodeVirusTypes = GetVirusTypesNode();
            System.Diagnostics.Debug.Assert(nodeVirusTypes != null);

            // Select all the <VirusType> node list
            System.Xml.XmlNodeList nodeVirusTypesList = nodeVirusTypes.SelectNodes(VirusType_Tag);
            foreach (System.Xml.XmlNode nodeVirusType in nodeVirusTypesList)
            {
                // Create a new instance
                IVirusType item = CreateVirusType();

                // Fill the instance with xml node
                if (!FillVirusTypeWithNode(item, nodeVirusType))
                {
                    // Do some error information
                    Logger.Warn("Invalid VirusType xml node, ignore it!");
                    continue;
                }

                // Add to the PatientReports list
                virusTypes.Add(item);
            }

            return virusTypes;
        }

        /// <summary>
        /// Select object by ID
        /// </summary>
        /// <param name="id">Object ID to find</param>
        /// <returns>The found object instance</returns>
        public IVirusType SelectByID(Guid id)
        {
            // Find the xml node by ID
            System.Xml.XmlNode nodeVirusType = FindNodeByID(id);
            if (nodeVirusType == null)
                return null;

            // Create a new instance
            IVirusType item = CreateVirusType();

            // Fill the instance with xml node
            if (!FillVirusTypeWithNode(item, nodeVirusType))
            {
                // Do some error information
                Logger.Error("Invalid virus type xml node!");
                return null;
            }

            return item;
        }

        /// <summary>
        /// Search the objects by specified filter 
        /// </summary>
        /// <param name="filter">The search filter</param>
        /// <returns>The found objects</returns>
        public IList<IVirusType> Search(ISearchFilter<IVirusType> filter)
        {
            return null;
        }

        /// <summary>
        /// Get or set the base cell count, all the virus CT calculation
        /// will base on this same value
        /// </summary>
        public UInt32 BaseCellCount 
        { 
            get 
            {
                System.Xml.XmlNode nodeVirusTypes = GetVirusTypesNode();
                System.Xml.XmlAttribute attr = nodeVirusTypes.Attributes[BaseCellCount_Tag];
                
                if (attr == null) // Not set, set to default
                {
                    this.BaseCellCount = Default_BaseCellCount;
                    return Default_BaseCellCount;
                }

                // Get from the attribute
                return UInt32.Parse(attr.Value);                
            }

            set
            {
                System.Xml.XmlNode nodeVirusTypes = GetVirusTypesNode();
                System.Xml.XmlAttribute attr = this._doc.CreateAttribute(BaseCellCount_Tag);
                attr.Value = value.ToString();
                nodeVirusTypes.Attributes.Append(attr);
                Save();
            }
        }

        /// <summary>
        /// Get or set the virus valve value
        /// if the virus Exponent > VirusSafetyExponent, it will be "阳性"
        /// else it will be "阴性"
        /// </summary>
        public double VirusSafetyExponent 
        {
            get
            {
                System.Xml.XmlNode nodeVirusTypes = GetVirusTypesNode();
                System.Xml.XmlAttribute attr = nodeVirusTypes.Attributes[VirusSafetyExponent_Tag];

                if (attr == null) // Not set, set to default
                {
                    this.VirusSafetyExponent = Default_VirusSafetyExponent;
                    return Default_VirusSafetyExponent;
                }

                // Get from the attribute
                return double.Parse(attr.Value);
            }

            set
            {
                System.Xml.XmlNode nodeVirusTypes = GetVirusTypesNode();
                System.Xml.XmlAttribute attr = this._doc.CreateAttribute(VirusSafetyExponent_Tag);
                attr.Value = value.ToString();
                nodeVirusTypes.Attributes.Append(attr);
                Save();
            } 
        }

        protected void AppendInternal(IVirusType item, System.Xml.XmlNode nodeVirusTypes)
        {
            System.Diagnostics.Debug.Assert(item != null);
            System.Diagnostics.Debug.Assert(nodeVirusTypes != null);

            // Check if it is existed, if existed just report error
            if (IsExisted(item, nodeVirusTypes))
            {
                System.Diagnostics.Debug.Assert(false,
                    string.Format("Duplicated Object ID {0}, Please check it!", item.ObjectID.ToString()));

                throw new ApplicationException(string.Format(
                    "Duplicated Object ID {0}, Please check it!",
                    item.ObjectID.ToString()));
            }

            // Then append to the xml file
            // Create a new <VirusType> node
            System.Xml.XmlElement elemVirusType = this._doc.CreateElement(VirusType_Tag);

            FillNodeWithVirusType(item, elemVirusType);

            nodeVirusTypes.AppendChild(elemVirusType);
        }

        protected bool FillNodeWithVirusType(IVirusType item, System.Xml.XmlNode elemVirusType)
        {
            System.Diagnostics.Debug.Assert(item != null);
            System.Diagnostics.Debug.Assert(elemVirusType != null);
            
            // Append <ObjectID> attribute
            System.Xml.XmlAttribute attr = this._doc.CreateAttribute(ObjectID_Tag);
            attr.Value = item.ObjectID.ToString();
            elemVirusType.Attributes.Append(attr);

            // Append <Name> attribute
            attr = this._doc.CreateAttribute(Name_Tag);
            attr.Value = item.Name;
            elemVirusType.Attributes.Append(attr);
            
            // Append <EVal> attribute
            attr = this._doc.CreateAttribute(EVal_Tag);
            attr.Value = item.EVal.ToString();
            elemVirusType.Attributes.Append(attr);

            // Append <KVal> attribute
            attr = this._doc.CreateAttribute(KVal_Tag);
            attr.Value = item.KVal.ToString();
            elemVirusType.Attributes.Append(attr);

            // Append <BVal> attribute
            attr = this._doc.CreateAttribute(BVal_Tag);
            attr.Value = item.BVal.ToString();
            elemVirusType.Attributes.Append(attr);

            // Append <DefineType> attribute
            attr = this._doc.CreateAttribute(DefineType_Tag);
            attr.Value = item.VirusDefineType.ToString();
            elemVirusType.Attributes.Append(attr);
            
            return true;
        }

        protected System.Xml.XmlNode GetVirusTypesNode()
        {
            System.Xml.XmlNode nodeRoot = this._doc.SelectSingleNode("PCRConfig");
            System.Diagnostics.Debug.Assert(nodeRoot != null, "PCRConfig xml tag can not be empty!");
            System.Xml.XmlNode nodeVirusTypes = nodeRoot.SelectSingleNode(VirusTypes_Tag);
            if (nodeVirusTypes == null) // Add the node if not existed
            {
                nodeVirusTypes = this._doc.CreateElement(VirusTypes_Tag);
                nodeRoot.AppendChild(nodeVirusTypes);
            }

            return nodeVirusTypes;
        }

        protected void Save()
        {
            this._doc.Save(this._filename);
        }

        protected bool IsExisted(IVirusType item, System.Xml.XmlNode nodeVirusType)
        {
            System.Diagnostics.Debug.Assert(item != null);
            System.Diagnostics.Debug.Assert(nodeVirusType != null);

            return false;
        }

        protected IVirusType CreateVirusType()
        {
            IVirusType virusType = ObjectFactory.CreateObject(ObjectType.VirusType) as IVirusType;
            System.Diagnostics.Debug.Assert(virusType != null);
            return virusType;
        }
        
        protected bool FillVirusTypeWithNode(IVirusType item, System.Xml.XmlNode node)
        {
            System.Diagnostics.Debug.Assert(item != null);
            System.Diagnostics.Debug.Assert(node != null);

            System.Xml.XmlAttribute attr;

            // Get <ObjectID> attribute
            attr = node.Attributes[ObjectID_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.ObjectID = new Guid(attr.Value);

            // Get <Name> attribute
            attr = node.Attributes[Name_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.Name = attr.Value;

            // Get <EVal> attribute
            attr = node.Attributes[EVal_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.EVal = double.Parse(attr.Value);

            // Get <KVal> attribute
            attr = node.Attributes[KVal_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.KVal = double.Parse(attr.Value);

            // Get <BVal> attribute
            attr = node.Attributes[BVal_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.BVal = double.Parse(attr.Value);

            // Get <VirusDefineType> attribute
            attr = node.Attributes[DefineType_Tag];
            System.Diagnostics.Debug.Assert(attr != null);
            item.VirusDefineType = (VirusDefineType)Enum.Parse(typeof(VirusDefineType), attr.Value);
            
            return true;
        }

        protected System.Xml.XmlNode FindNodeByID(Guid id)
        {
            System.Xml.XmlNode nodeVirusTypes = GetVirusTypesNode();
            System.Diagnostics.Debug.Assert(nodeVirusTypes != null);

            // Select all the <VirusType> node list
            System.Xml.XmlNodeList nodeVirusTypesList = nodeVirusTypes.SelectNodes(VirusType_Tag);
            foreach (System.Xml.XmlNode nodeVirusType in nodeVirusTypesList)
            {
                // Judge the ID
                System.Xml.XmlAttribute objAttr = nodeVirusType.Attributes[ObjectID_Tag];
                System.Diagnostics.Debug.Assert(objAttr != null);

                if (objAttr.Value == id.ToString())
                {
                    return nodeVirusType;
                }
            }
            
            return null;
        }

        protected ILogger Logger
        {
            get
            {
                return PCRRoot.Instance.LogManager.GetLogger(typeof(XmlPatientReport));
            }
        }
    }
}
